package org.nobject.common.js;

import static org.nobject.common.lang.ClassUtils.isBoolean;
import static org.nobject.common.lang.ClassUtils.isChar;
import static org.nobject.common.lang.ClassUtils.isCollection;
import static org.nobject.common.lang.ClassUtils.isDate;
import static org.nobject.common.lang.ClassUtils.isMap;
import static org.nobject.common.lang.ClassUtils.isNumber;
import static org.nobject.common.lang.ClassUtils.isString;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.nobject.common.bean.BeanUtils;
import org.nobject.common.exception.ConvertException;
import org.nobject.common.file.FileUtils;
import org.nobject.common.lang.ByteUtils.ASC;
import org.nobject.common.lang.ClassUtils;
import org.nobject.common.lang.ObjectUtils;
import org.nobject.common.lang.StringUtils;

/**
 * JSON工具类<br>
 * 
 * 核心1:JSON字符串到对象的转换(Map,List)
 * 核心2:对象到JSON字符串的转换
 * 
 * 支持键值带双引号的标准格式，同时支持不带双引号的非标准格式，键值引号可以用单引号也可以用双引号
 * 
 * @author bianrongjun
 * @version 1.0
 */
public class JSONUtils {

	/** 是否为空字符 */
	private static Character[] emptyChars=new Character[] { (char)ASC.cr, (char)ASC.lf, (char)ASC.space, (char)ASC.tab };
	
	/** 状态 */
	private enum STATUS {
		begin, partBegin, keyBegin, keyEnd, valueBegin, partEnd, end
	}
	
	/** 
	 * 是否为空字符
	 * @param c
	 */
	private static boolean isEmpty(char c){
		return ObjectUtils.in(c,emptyChars);
	}
	
	/******************************************************************************************************************
	 * 
	 *  解析:parse
	 * 
	 *******************************************************************************************************************/
	
	/** 
	 * 解析
	 * @param cs 字符串
	 * @param idx 当前索引
	 */
	private static Object[] parse(char[] cs,int idx) throws ConvertException{
		for(int i=idx;i<cs.length;i++){
			char c=cs[i];
			//System.out.println("read:"+i+","+c);
			if(isEmpty(c)) continue;
			
			if(c==ASC.bracket_l)							return parseArray(cs, i);
			else if(c==ASC.brace_l)							return parseObject(cs, i);
			else if(c==ASC.quot_d||c==ASC.quot_s)			return parseString(cs, i);
			else if(('0'<= c&& c <='9')||c=='+'||c=='-')	return parseNum(cs, i);
			else{
				if (c == 'n') {
					if (i+3<cs.length||new String(cs,i,3).equals("null")) return parseNull(cs, i);
				} else if(c=='t'||c=='f') {
					if ((i+3<cs.length&&new String(cs,i,4).equals("true"))
					  ||(i+4<cs.length&&new String(cs,i,5).equals("false"))){
						return parseBoolean(cs, i);
					}
				}
			}
		}
		throw new ConvertException("格式化无法识别");
	}
	
public static void main(String[] args) throws Exception{
		
		String s=FileUtils.read2String("c:/a.txt");
		JSONArray ja=new JSONArray(s);
		System.out.println(ja.getMap(1).get("questionStem"));
//		String u="0000225";
//		System.out.println(String.valueOf(u));
	}
	
	/** 
	 * 解析字符串
	 * @param cs 字符串
	 * @param idx 当前索引
	 */
	private static Object[] parseString(char[] cs,int idx) throws ConvertException{
		StringBuffer sb=new StringBuffer();
		char beginQuot=cs[idx];
		//System.out.println("readString:"+idx+","+beginQuot);
		idx++;
		boolean beginSlash=false;
		for(int i=idx;i<cs.length;i++){
			char c=cs[i];
			if(!beginSlash&&c==beginQuot){
				return new Object[]{sb.toString(),i};
			}else{
				if(!beginSlash&&c==ASC.slash_r){
					beginSlash=true;
				}else{
					if(beginSlash){
						String nc=null;
						if(c=='n')		{nc="\n";}
						else if(c=='r')	{nc="\r";}
						else if(c=='t')	{nc="\t";}
						else {
							nc=c+"";//TODO find more
						}
						if(nc!=null) sb.append(nc);
					}else{
						sb.append(c);
					}
					beginSlash=false;
				}
			}
		}
		throw new ConvertException("String格式化无法识别");
	}
	
	/** 
	 * 解析数字
	 * @param cs 字符串
	 * @param idx 当前索引
	 */
	private static Object[] parseNum(char[] cs,int idx) throws ConvertException{
		boolean doubleNum	=false;
		boolean start		=false;
		StringBuffer sb=new StringBuffer();
		if(cs[idx]=='+'||cs[idx]=='-'){
			sb.append(cs[idx]);
			idx++;
		}
		for(int i=idx;i<cs.length;i++){
			char c=cs[i];
			
			if(!doubleNum&&c==ASC.dot){
				if(!start) throw new ConvertException("格式错误");
				doubleNum=true;
				sb.append(c);
				continue;
			}
			
			if(c>='0'&&c<='9'){
				sb.append(c);
				if(!start) start=true;
			}else{
				Object o=null;
				if(doubleNum){
					o=Double.parseDouble(sb.toString());
				}else{
					if(sb.length()>9){
						o=Long.parseLong(sb.toString());
					}else{
						o=Integer.parseInt(sb.toString());
					}
					
				}
				return new Object[]{o,i-1};
			}
		}
		
		throw new ConvertException("Num格式化无法识别");
	}
	
	/** 
	 * 解析NULL
	 * @param cs
	 * @param idx
	 * @return
	 */
	private static Object[] parseNull(char[] cs,int idx){
		return new Object[]{null,idx+3};
	}
	
	/** 
	 * 解析布尔
	 * @param cs
	 * @param idx
	 */
	private static Object[] parseBoolean(char[] cs,int idx){
		boolean t=cs[idx]=='t';
		int c=t?3:4;
		return new Object[]{t,idx+c};
	}
	
	/** 
	 * 解析数组
	 * @param cs 字符串
	 * @param idx 当前索引
	 */
	private static Object[] parseArray(char[] cs,int idx) throws ConvertException{
		List l=new LinkedList();
		STATUS status 			= STATUS.begin;
		
		for(int i=idx;i<cs.length;i++){
			char c=cs[i];
			//System.out.println("readArray:"+i+","+c);
			switch (status) {
				case begin:
					if(c == ASC.bracket_l){
						status=STATUS.partBegin;
						continue;
					}
				case partBegin:
					if(isEmpty(c)) continue;
					if(c== ASC.bracket_r){
						return new Object[]{l,i};
					}else{
						Object[] o=parse(cs, i);
						i=(Integer)o[1];
						l.add(o[0]);
						status=STATUS.partEnd;
						continue;
					}
				case partEnd:
					if(isEmpty(c)) continue;
					if(c==ASC.comma){
						status=STATUS.partBegin;
						continue;
					}else if(c==ASC.bracket_r){
						return new Object[]{l,i};
					}
			}
		}
		
		throw new ConvertException("List转换格式有误:"+new String(cs));
	}
	
	/** 
	 * 解析对象
	 * @param cs 字符串
	 * @param idx 当前索引
	 */
	private static Object[] parseObject(char[] cs,int idx) throws ConvertException{
		Map o=new LinkedHashMap();
		StringBuffer sb_key=new StringBuffer();
		boolean withKeyQuot=false;
		char keyQuot=(char)ASC.quot_d;
		STATUS status 			= STATUS.begin;
		
		for(int i=idx;i<cs.length;i++){
			char c=cs[i];
			//System.out.println("readObject:"+i+","+c);
			switch (status) {
				case begin:
					if(c == ASC.brace_l){
						status=STATUS.partBegin;
						continue;
					}
				case partBegin:
					if(isEmpty(c)) continue;
					if(c== ASC.brace_r){
						return new Object[]{o,i};
					}else if(('a'<=c&&c<='z')||('A'<=c&&c<='Z')||c =='_'||c=='$'||c==ASC.quot_d||c==ASC.quot_s){
						status=STATUS.keyBegin;
						if(c==ASC.quot_d||c==ASC.quot_s){
							withKeyQuot=true;
							keyQuot=c;
						}else{
							sb_key.append(c);
						}
						continue;
					}
				case keyBegin:
					if( (withKeyQuot&&32<=c&&c<=126&&c!=keyQuot)
							||
					    (!withKeyQuot&&(('a'<=c&&c<='z')||('A'<= c&&c<= 'Z')||c=='_'||c=='$'||(c>='0'&&c<='9')))){
						sb_key.append(c);
					}else if(c==keyQuot){
						status=STATUS.keyEnd;
					}else if(isEmpty(c)){
						if(withKeyQuot) throw new ConvertException("未匹配键引号:"+c);
						status=STATUS.keyEnd;
					}else if(c==ASC.colon){
						status=STATUS.valueBegin;
					}else{
						throw new ConvertException("非键值可用字符:"+c);
					}
					continue;
				case keyEnd:
					if(isEmpty(c)) continue;
					else if(c==ASC.colon){
						status=STATUS.valueBegin;
						continue;
					}
				case valueBegin:
					if(isEmpty(c)) continue;
					else{
						Object[] vo=parse(cs, i);
						o.put(sb_key.toString(), vo[0]);
						sb_key=new StringBuffer();
						i=(Integer)vo[1];
						status=STATUS.partEnd;
					}
				case partEnd:
					if(isEmpty(c)) continue;
					if(c==ASC.comma){
						status=STATUS.partBegin;
						continue;
					}else if(c== ASC.brace_r){
						return new Object[]{o,i};
					}
			}
		}
		
		throw new ConvertException("Map转换格式有误:"+new String(cs));
	}
	
	
	/******************************************************************************************************************
	 * 
	 *  静态 区域
	 * 
	 *******************************************************************************************************************/
	
	/** 
	 * 对象转换为JSON格式字符串
	 * @param obj 对象
	 */
	public static String toString(Object obj) {
		if(obj==null) return "null";
		StringBuffer sb = new StringBuffer();
		Class clazz = obj.getClass();
		if (clazz.isArray()) {
			int l=Array.getLength(obj);
			sb.append("[");
			for (int i = 0;i< l; i++) {
				if(i!=0) sb.append(",");
				sb.append(toString(Array.get(obj, i)));
			}
			sb.append("]");
		}else if (isMap(clazz)) {
			Map m = (Map) obj;
			sb.append("{");
			for (Iterator iterator = m.keySet().iterator(); iterator.hasNext();) {
				Object k=iterator.next();
				String key = StringUtils.toString0(k) ;
				Object o=m.get(k);
				sb.append("\"").append(key).append("\"").append(":").append(toString(o));
				if (iterator.hasNext()) sb.append(",");
			}
			sb.append("}");
		} else if (isCollection(clazz)) {
			Collection l = (Collection) obj;
			sb.append("[");
			for (Iterator iterator = l.iterator(); iterator.hasNext();) {
				sb.append(toString(iterator.next()));
				if (iterator.hasNext()) sb.append(",");
			}
			sb.append("]");
		} else if (isString(clazz)||isChar(clazz)) {
			String s=isString(clazz)?(String)obj:obj.toString();
			sb.append("\"")
				.append(s	.replaceAll("\\\\","\\\\\\\\")
							.replaceAll("\"","\\\\\"")
							.replaceAll("\r","\\\\r")
							.replaceAll("\n","\\\\n")
							.replaceAll("\t","\\\\t"))
				.append("\"");
		} 
		else if (isNumber(clazz)) 	sb.append(StringUtils.toString((Number)obj));
		else if (isBoolean(clazz)) 	sb.append(StringUtils.toString((Boolean)obj));
		else if (isDate(clazz)) 	sb.append(StringUtils.toString((Date)obj));
		else if (JSONArray.class.isAssignableFrom(clazz)||JSONObject.class.isAssignableFrom(clazz)) sb.append(obj.toString());
		else {
			Field[] fields=ClassUtils.getFields(clazz);
			sb.append("{");
			boolean has=false;
			for (Field field:fields) {
				String propName = field.getName();
				try {
					Method method = (Method) BeanUtils.getGetMethod(obj.getClass(), propName);
					if(method!=null){
						if(has) sb.append(",");
						has=true;
						sb.append("\""+propName+"\"").append(":");
						Object ro = method.invoke(obj);
						if (ro == null)  sb.append("null");
						else sb.append(toString(ro));
					}
				} catch (Exception e) {
					throw new RuntimeException("不该发生的异常:"+e.getMessage());
				}
			}
			sb.append("}");
		}
		return sb.toString();
	}
	
	/**
	 * 字符串转换成JSON数组
	 * @param str 字符串
	 */
	public static List toList(String str) 				throws ConvertException{return  (List)parseArray(str.toCharArray(), 0)[0];}

	/**
	 * 字符串转换成JSON对象
	 * @param str 字符串
	 */
	public static Map toMap(String str) 				throws ConvertException{return  (Map)parseObject(str.toCharArray(), 0)[0];}
	
	/** 
	 * 字符串转换为JSONObject
	 * @param str 字符串
	 */
	public static JSONObject toJSONObject(String str) 	throws ConvertException{return new JSONObject(JSONUtils.toMap(str));}
	
	/** 
	 * 字符串转换为JSONArray
	 * @param str 字符串
	 */
	public static JSONArray toJSONArray(String str) 	throws ConvertException{return new JSONArray(JSONUtils.toList(str));}
	
	
	/**
     * 格式化json串(System.out用)
     * @param json json串
     * @param space 占位符
     */ 
    public static String format(String json, String space) { 
        if (StringUtils.isEmpty(json)) return null; 
         
        int fixedLenth = 0; 
        ArrayList<String> tokenList = new ArrayList<String>(); 
        { 
            String jsonTemp = json; 
            //预读取 
            while (jsonTemp.length() > 0) { 
                String token = format_getToken(jsonTemp); 
                jsonTemp = jsonTemp.substring(token.length()); 
                token = token.trim(); 
                tokenList.add(token); 
            }            
        } 
         
        for (int i = 0; i < tokenList.size(); i++) { 
            String token = tokenList.get(i); 
            int length = token.getBytes().length; 
            if (length > fixedLenth && i < tokenList.size() - 1 && tokenList.get(i + 1).equals(":")) { 
                fixedLenth = length; 
            } 
        } 
         
        StringBuilder buf = new StringBuilder(); 
        int count = 0; 
        for (int i = 0; i < tokenList.size(); i++) { 
             
            String token = tokenList.get(i); 
             
            if (token.equals(",")) { 
                buf.append(token); 
                format_doFill(buf, count, space); 
                continue; 
            } 
            if (token.equals(":")) { 
                buf.append(" ").append(token).append(" "); 
                continue; 
            } 
            if (token.equals("{")) { 
                String nextToken = tokenList.get(i + 1); 
                if (nextToken.equals("}")) { 
                    i++; 
                    buf.append("{ }"); 
                } else { 
                    count++; 
                    buf.append(token); 
                    format_doFill(buf, count, space); 
                } 
                continue; 
            } 
            if (token.equals("}")) { 
                count--; 
                format_doFill(buf, count, space); 
                buf.append(token); 
                continue; 
            } 
            if (token.equals("[")) { 
                String nextToken = tokenList.get(i + 1); 
                if (nextToken.equals("]")) { 
                    i++; 
                    buf.append("[ ]"); 
                } else { 
                    count++; 
                    buf.append(token); 
                    format_doFill(buf, count, space); 
                } 
                continue; 
            } 
            if (token.equals("]")) { 
                count--; 
                format_doFill(buf, count, space); 
                buf.append(token); 
                continue; 
            } 
             
            buf.append(token); 
            
            //左对齐 
            if (i < tokenList.size() - 1 && tokenList.get(i + 1).equals(":")) { 
                int fillLength = fixedLenth - token.getBytes().length; 
                if (fillLength > 0) { 
                    for(int j = 0; j < fillLength; j++) { 
                        buf.append(" "); 
                    } 
                } 
            } 
        } 
        return buf.toString(); 
    }
    
    private static void format_doFill(StringBuilder buf, int count, String fillStringUnit) { 
        buf.append("\n"); 
        for (int i = 0; i < count; i++) { 
            buf.append(fillStringUnit); 
        } 
    }
     
    /** 
     * getToken
     * @param json
     */
    private static String format_getToken(String json) { 
        StringBuilder buf = new StringBuilder(); 
        boolean isBeginQuote = false; 
        while (json.length() > 0) { 
            String token = json.substring(0, 1); 
            json = json.substring(1); 
             
            if (!isBeginQuote && ObjectUtils.in(token, new Object[]{":","{","}","[","]",","})) { 
                if (buf.toString().trim().length() == 0) {                   
                    buf.append(token); 
                } 
                break; 
            } 
 
            if (token.equals("\\")) { 
                buf.append(token); 
                buf.append(json.substring(0, 1)); 
                json = json.substring(1); 
                continue; 
            } 
            if (token.equals("\"")) { 
                buf.append(token); 
                if (isBeginQuote) { 
                    break; 
                } else { 
                    isBeginQuote = true; 
                    continue; 
                }                
            } 
            buf.append(token); 
        } 
        return buf.toString(); 
    } 
	
	public static void main2(String[] args) throws Exception {
		
//		String ss="222\\\\'";
//		System.out.println(ss);
		
		String s="{'a':-2222.1a'}";
		
//		System.out.println(s);
		JSONObject jo=new JSONObject(s);
		System.out.println(jo.getString("a"));
		
//		String s=FileUtils.read2String("c:/a.txt");
//		if(s.trim().startsWith("{")){
//			JSONObject jo=new JSONObject(s);
//			System.out.println(jo.toString());
//		}else if(s.trim().startsWith("[")){
//			JSONArray jo=new JSONArray(s);
//			System.out.println(jo.toString());
//		}else throw new Exception("格式错误");
		
//		JSONObject jo=new JSONObject();
//		jo._put("aa",null);
//		System.out.println(jo.toString());
	}

}